package com.baofu.paycore.biz.paycore.handler.impl;

import com.baofu.clear.service.facade.enums.EventTypeEnum;
import com.baofu.clear.service.facade.model.resp.NotifyDTO;
import com.baofu.paycore.biz.engine.result.CommandResult;
import com.baofu.paycore.biz.external.account.AccountBaseService;
import com.baofu.paycore.biz.external.account.models.AccountFundResBO;
import com.baofu.paycore.biz.external.clear.ClearQueryService;
import com.baofu.paycore.biz.external.clear.ClearRechargeService;
import com.baofu.paycore.biz.external.clear.EventService;
import com.baofu.paycore.biz.external.clear.converter.ClearResultConverter;
import com.baofu.paycore.biz.external.clear.model.ClearFundResBO;
import com.baofu.paycore.biz.external.clear.model.EventRuleResBO;
import com.baofu.paycore.biz.model.BackPayBO;
import com.baofu.paycore.biz.notice.ProductHandler;
import com.baofu.paycore.biz.paycore.converter.BO2BOConverter;
import com.baofu.paycore.biz.paycore.handler.BaseHandler;
import com.baofu.paycore.common.enums.FundTypeEnum;
import com.baofu.paycore.common.exception.PayCoreBizException;
import com.baofu.paycore.dal.model.ClearAccountDO;
import com.baofu.paycore.dal.model.ClearDO;
import com.baofu.paycore.manager.converter.BO2DOConverter;
import com.baofu.paycore.manager.model.RechargeBO;
import com.baofu.paycore.manager.model.RechargeFundBO;
import com.baofu.paycore.manager.model.ResponseBO;
import com.baofu.paycore.manager.storage.impl.ClearAccountManager;
import com.baofu.paycore.manager.storage.impl.ClearManager;
import com.baofu.paycore.manager.storer.RechargeOrderRepository;
import com.baofu.paycore.service.facade.enums.PayCoreErrorCode;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import com.system.commons.exception.BizServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 充值订单处理
 *
 * <p/>
 *      1、同步充值
 * </p>
 * User: LZQ Date: 2016/04/20  Version: 1.0
 */
@Slf4j
@Service
public class RechargeHandler extends BaseHandler<RechargeBO,NotifyDTO> {

    /**
     * 持久化服务
     */
    @Autowired
    private RechargeOrderRepository rechargeOrderRepository;

    /**
     * 清分充值服务
     */
    @Autowired
    private ClearRechargeService clearRechargeService;

    /**
     * 账务充值
     */
    @Autowired
    protected AccountBaseService accountRechargeServiceImpl;

    /**
     * 会计事件服务
     */
    @Autowired
    private EventService eventService;

    /**
     * 充值资金服务
     */
    @Autowired
    private ClearAccountManager clearAccountManager;

    /**
     * 充值主单服务
     */
    @Autowired
    private ClearManager clearManager;

    /**
     * 清分查询服务
     */
    @Autowired
    private ClearQueryService clearQueryService;

    /** 发kafka消息服务 */
    @Autowired
    private ProductHandler productHandler;

    /**
     * 充值处理
     * @param rechargeBO        充值参数
     * @return                  响应
     */
    public BackPayBO process(RechargeBO rechargeBO){
        log.debug("开始处理充值命令：{}", rechargeBO);
        doExecute(rechargeBO);
        return BO2BOConverter.rechargeBO2BackPayBO(rechargeBO);
    }

    /**
     * 激活领域对象
     *
     * @param orderId 订单ID
     * @return 订单对象
     */
    @Override
    public RechargeBO activeOrder(String orderId) {
        RechargeBO rechargeBO = rechargeOrderRepository.load(orderId);
        if(rechargeBO == null){
            log.error("找不到充值订单:{}",orderId);
            throw new PayCoreBizException(PayCoreErrorCode.ORDER_IS_NOT_EXISTED);
        }
        return rechargeBO;
    }

    /**
     * 查询订单信息
     *
     * @param payNo   支付单号
     * @return        订单对象
     */
    public RechargeBO queryOrderByPayNo(String payNo){
        RechargeBO rechargeBO = rechargeOrderRepository.loadOrderByPayNo(payNo);
        if(rechargeBO == null){
            log.error("找不到充值订单:{}", payNo);
            throw new PayCoreBizException(PayCoreErrorCode.ORDER_IS_NOT_EXISTED);
        }
        return rechargeBO;
    }

    /**
     * 前置检查
     *
     * @param order 订单
     */
    @Override
    protected void checkBeforeProcess(RechargeBO order) {
        order.checkStatus();
        if(!FundTypeEnum.RECHARGE.equals(order.getRechargeFundBO().get(0).getFundType())){
            log.info("充值单RechargeBO:{}", order);
            throw new BizServiceException(PayCoreErrorCode.RECHARGE_FUND_SORT_ERROR);
        }
    }

    /**
     * 生成指令
     *
     * @param order  充值请求对象
     */
    @Override
    protected void processOrders(RechargeBO order) {

        //调用清分系统的支付接口
        ClearFundResBO clearFundResBO = clearRechargeService.fundHandle(order);
        //根据清分系统返回的结果对象转充值请求对象
        ClearResultConverter.result2RechargeBO(clearFundResBO, order);

        log.debug("调用清分系统的支付接口返回结果, RechargeBO:{}",order);

        /**
         * 根据清分返回结果中，是否为真实结果的字段进行流程判断处理：
         * 1、如果返回的是真实结果，那么，需要进行后续调用账户流程;
         * 2、如果返回的不是真实结果，那么就无需进行后续流程操作，也无需更新订单的状态，直接返回，并等待清分回调的真实结果
         */
        if(!order.isRealFlag()){
            return ;
        }

        //如果clear返回的状态是fail，则无需在调用账务进行充值
        if(!PayStatusEnums.SUCCESS.getCode().equals(order.getRechargeBankBO().getStatus())){
            order.setStatus(clearFundResBO.getPayStat());
            return;
        }
        advance(order);
    }

    /**
     * 订单推进
     *
     * @param order         充值订单
     */
    void advance(RechargeBO order){
        //账务充值资金单
        List<RechargeFundBO> rechargeFundBOs = order.getRechargeFundBO();
        RechargeFundBO rechargeFundBO = order.getRechargeFund(FundTypeEnum.RECHARGE.getCode());
        if (FundTypeEnum.FEE.equals(rechargeFundBO.getFundType())){
            log.error("充值流程 订单推进有误 先处理充值单后流程手续费转账单 资金单:{}", rechargeFundBO);
            return;
        }
        rechargeFundBO.setEventNo(StringUtils.isEmpty(rechargeFundBO.getEventNo()) ? order.getEventNo() :
                rechargeFundBO.getEventNo());
        //充值
        if (!rechargeProcessor(order, rechargeFundBO)) return;

        if (rechargeFundBOs.size() > 1){
            RechargeFundBO rechargeFee = order.getRechargeFund(FundTypeEnum.FEE.getCode());
            //手续费转账
            if(!feeProcessor(order, rechargeFee)) return;
        }

    }

    /**
     * 充值流程处理
     *
     * @param rechargeBO        充值单
     * @param rechargeFundBO    充值资金单
     * @return                  流程成功与否
     */
    private boolean rechargeProcessor(RechargeBO rechargeBO, RechargeFundBO rechargeFundBO){
        //判断清分是否返回会计事件
        if (StringUtils.isEmpty(rechargeFundBO.getEventNo())) {
            eventProcess(rechargeBO, rechargeFundBO);
            //判断订单状态
            if (!PayStatusEnums.SUCCESS.getCode().equals(rechargeBO.getStatus())) {
                failFundProcess(rechargeBO, rechargeFundBO);
                return false;
            }
        }
        //调用账务充值接口
        AccountFundResBO accountFundResBO = accountRechargeServiceImpl.fundHandle(rechargeBO, rechargeFundBO);
        accountRechargeServiceImpl.afterRecharge(accountFundResBO, rechargeBO, rechargeFundBO);
        if (!PayStatusEnums.SUCCESS.getCode().equals(rechargeFundBO.getStatus())) {
            failFundProcess(rechargeBO, rechargeFundBO);
            return false;
        }
        return true;
    }

    /**
     * 手续费转账处理流程
     *
     * @param order             充值单
     * @param rechargeFundBO    手续费资金单
     * @return                  流程成功与否
     */
    private boolean feeProcessor(RechargeBO order, RechargeFundBO rechargeFundBO){
        //调用账务转账接口
        AccountFundResBO accountFundResBO = accountRechargeServiceImpl.feeFundHandle(order, rechargeFundBO);
        accountRechargeServiceImpl.afterRecharge(accountFundResBO, order, rechargeFundBO);
        if (!PayStatusEnums.SUCCESS.getCode().equals(rechargeFundBO.getStatus())) {
            failFundProcess(order, rechargeFundBO);
            return false;
        }
        return true;
    }

    /**
     * 持久化对象
     *
     * @param order 单据
     */
    @Override
    protected void reStore(RechargeBO order) {
        //更新充值订单
        rechargeOrderRepository.update(order);
    }

    /**
     * 指令完成后处理
     *
     * @param order     领域模型
     * @param cmdResult 指令处理结果
     */
    @Override
    protected void processAfter(RechargeBO order, Map<String, CommandResult> cmdResult) {

    }

    /**
     * 失败资金单流程处理
     *
     * @param order    充值请求对象
     */
    private void failFundProcess(RechargeBO order, RechargeFundBO rechargeFundBO){
        //失败流程订单状态设置
        order.setStatus(rechargeFundBO.getStatus());
        order.setErrorCode(rechargeFundBO.getErrorCode());
        order.setErrorMsg(rechargeFundBO.getErrorReason());
        rechargeFundBO.setStatus(rechargeFundBO.getStatus());
    }

    /**
     * 会计事件处理流程
     *
     * @param order         充值订单
     */
    private void eventProcess(RechargeBO order,RechargeFundBO rechargeFundBO){

        //查询会计事件号
        EventRuleResBO eventRuleResBO = eventService.queryEventNo(EventTypeEnum.CHANNEL,
                order.getRechargeBankBO().getSubFundChannel());
        BO2BOConverter.eventResBO2RechargeBO(eventRuleResBO, order, rechargeFundBO);
    }

    /**
     * 需重试接口 对内部资金单的重试（订单状态推进）
     *
     * @param rechargeBO 充值订单对象
     * @return ResponseBO
     * @throws RuntimeException 出现异常时表示重试没有成功（子类方法实现中不可捕获异常）
     */
    public ResponseBO dalInnerFundProc(RechargeBO rechargeBO) throws RuntimeException {
        ResponseBO responseBO = null;
        log.info("重试充值 充值主单号:{} 主单状态:{}", rechargeBO.getPayNo(), rechargeBO.getStatus());
        if (PayStatusEnums.SUCCESS.getCode().equals(rechargeBO.getStatus()) ||
                PayStatusEnums.FAIL.getCode().equals(rechargeBO.getStatus())){
            return new ResponseBO(rechargeBO.getStatus(), rechargeBO.getErrorCode(), rechargeBO.getErrorMsg(), null,
                    true);
        }
        //先判断银行单是否成功,成功处理后面流程,不成功不处理
        if (!PayStatusEnums.SUCCESS.getCode().equals(rechargeBO.getRechargeBankBO().getStatus())){
            log.error("充值重试 银行单未成功 支付单号:{} 银行单号:{} 银行单状态:{}", rechargeBO.getPayNo(),
                    rechargeBO.getRechargeBankBO().getFundDetailNo(), rechargeBO.getRechargeBankBO().getStatus());
            return null;
        }
        //充值资金单
        List<RechargeFundBO> rechargeFundBOs = rechargeBO.getRechargeFundBO();
        for (RechargeFundBO rechargeFund : rechargeFundBOs){
            if (PayStatusEnums.SUCCESS.getCode().equals(rechargeFund.getStatus())){
                log.debug("充值重试 资金单类型:{} 资金单号:{} 资金单状态:{}", rechargeFund.getFundType(),
                        rechargeFund.getFundDetailNo(), rechargeFund.getStatus());
                continue;
            }
            if (PayStatusEnums.FAIL.getCode().equals(rechargeFund.getStatus())){
                log.error("充值重试 资金单类型:{} 资金单号:{} 资金单状态:{}", rechargeFund.getFundType(),
                        rechargeFund.getFundDetailNo(), rechargeFund.getStatus());
                break;
            }
            switch (rechargeFund.getFundType()){
                case RECHARGE:
                    log.debug("充值重试 充值资金单类型:{} 资金单号:{} 资金单状态:{}", rechargeFund.getFundType(),
                            rechargeFund.getFundDetailNo(), rechargeFund.getStatus());
                    this.rechargeProcessor(rechargeBO, rechargeFund);
                    responseBO = this.handleRecharge(rechargeFund);
                    break;
                case FEE:
                    log.debug("充值重试 手续费资金单类型:{} 资金单号:{} 资金单状态:{}", rechargeFund.getFundType(),
                            rechargeFund.getFundDetailNo(), rechargeFund.getStatus());
                    this.feeProcessor(rechargeBO, rechargeFund);
                    responseBO = this.handleRecharge(rechargeFund);
                    break;
                default:
                    log.error("暂不支持此业务：{}", rechargeFund.getFundType());
                    throw new PayCoreBizException(PayCoreErrorCode.NOT_IN_SERVICE);
            }
            if (!validateContinue(rechargeBO.isRealFlag(), rechargeFund.getStatus())){
                log.error("重试 充值失败 请求服务类型:{} 资金单号:{} 资金单类型:{} 转账处理状态:{}", rechargeBO.getServiceCode(),
                        rechargeFund.getFundDetailNo(), rechargeFund.getFundType(), rechargeFund.getStatus());
                break;
            }
        }
        log.info("开始更新T_PC_CLEAR 状态:{}",  rechargeBO.getStatus());
        handleRechargeStatus(responseBO, rechargeBO);
        return responseBO;
    }

    /**
     * 充值处理
     *
     * @param rechargeFundBO    充值资金单
     * @return                  responseBO
     */
    private ResponseBO handleRecharge(RechargeFundBO rechargeFundBO){
        ClearAccountDO clearAccountDO = BO2DOConverter.getClearAccountDO(rechargeFundBO);
        clearAccountManager.modifyOrder(clearAccountDO);

        return new ResponseBO(rechargeFundBO.getStatus(), rechargeFundBO.getErrorCode(),
                rechargeFundBO.getErrorReason(), rechargeFundBO.getTransDate(), rechargeFundBO.getTransReceiptNo(),
                rechargeFundBO.getFundDetailNo(), true);
    }

    /**
     * 处理充值主单状态
     *
     * @param responseBO    响应单
     * @param rechargeBO    充值单
     */
    private void handleRechargeStatus(ResponseBO responseBO, RechargeBO rechargeBO){
        if (!PayStatusEnums.SUCCESS.getCode().equals(responseBO.getStatus())){
            return;
        }
        ClearDO clearDO = new ClearDO();
        clearDO.setPayNo(rechargeBO.getPayNo());
        clearDO.setStatus(responseBO.getStatus());
        clearManager.modifyOrder(clearDO);
    }

    /**
     * 充值超时流程处理
     *
     * @param requestOrderNo    请求订单号
     * @return                  responseBO
     */
    public ResponseBO handleRechargeTimeOut(RechargeBO rechargeBO, String requestOrderNo) {
        ClearFundResBO clearFundResBO = clearQueryService.queryByRequestOrderNo(rechargeBO.getRechargeBankBO().getPayNo(),
                rechargeBO.getRechargeBankBO().getFundDetailNo());
        //没有查询到值,支付失败
        if (PayCoreErrorCode.EMPTY_QUERY_RESULT.getErrorCode().equals(clearFundResBO.getErrorCode())) {
            this.failProcess(rechargeBO);
            reStore(rechargeBO);
            return new ResponseBO(PayStatusEnums.FAIL.getCode(), PayCoreErrorCode.CLEAR_ORDER_NOT_EXIST.getErrorCode(),
                    PayCoreErrorCode.CLEAR_ORDER_NOT_EXIST.getErrorDesc(), new Date(), null, requestOrderNo, true);
        }
        if(clearFundResBO.isRealFlag() && PayStatusEnums.FAIL.getCode().equals(clearFundResBO.getPayStat())){
            this.failProcess(rechargeBO);
            reStore(rechargeBO);
            return new ResponseBO(PayStatusEnums.FAIL.getCode(), clearFundResBO.getErrorCode(),
                    clearFundResBO.getErrorMsg(), new Date(), null, requestOrderNo, true);
        }

        //清分成功,走后面流程
        if (clearFundResBO.isRealFlag() && (PayStatusEnums.SUCCESS.getCode().equals(clearFundResBO.getPayStat()))) {
            //获取清分返回会计事件
            rechargeBO.setEventNo(clearFundResBO.getEventNo());
            //推进订单
            advance(rechargeBO);
            reStore(rechargeBO);
            return new ResponseBO(rechargeBO.getStatus(), rechargeBO.getErrorCode(),
                    rechargeBO.getErrorMsg(), clearFundResBO.getResponseDate(), clearFundResBO.getResponseNo(),
                    requestOrderNo, true);
        }

        return new ResponseBO(PayStatusEnums.INIT.getCode(), clearFundResBO.getErrorCode(),
                clearFundResBO.getErrorMsg(), clearFundResBO.getResponseDate(), clearFundResBO.getResponseNo(),
                requestOrderNo, false);
    }

    /**
     * 充值单失败状态处理
     *
     * @param rechargeBO    充值单
     */
    private void failProcess(RechargeBO rechargeBO){
        rechargeBO.setStatus(PayStatusEnums.FAIL.getCode());
        rechargeBO.getRechargeBankBO().setStatus(PayStatusEnums.FAIL.getCode());
        for(RechargeFundBO rechargeFundBO : rechargeBO.getRechargeFundBO()){
            rechargeFundBO.setStatus(PayStatusEnums.FAIL.getCode());
        }
    }
    /**
     * 订单补单具体操作
     */
    public String processRetry(RechargeBO rechargeBO){
        RechargeFundBO rechargeFund=rechargeBO.getRechargeFund(FundTypeEnum.RECHARGE.getCode());
        if(PayStatusEnums.FAIL.getCode().equals(rechargeFund.getStatus())){
            //订单入表
            rechargeOrderRepository.addRetryOrder(rechargeFund,rechargeBO.getOperator());
            //调用账务解冻提现
            //充值
           rechargeProcessor(rechargeBO, rechargeFund);
        }


        //解冻转账
        RechargeFundBO rechargeFee = rechargeBO.getRechargeFund(FundTypeEnum.FEE.getCode());
        if (rechargeFee != null&&PayStatusEnums.FAIL.getCode().equals(rechargeFee.getStatus())) {
            //订单入表
            rechargeOrderRepository.addRetryOrder(rechargeFee,rechargeBO.getOperator());
            //手续费转账
            feeProcessor(rechargeBO, rechargeFee);
        }
        handleRetryStatus(rechargeBO,rechargeFee,rechargeFund);
        rechargeOrderRepository.updateRetry(rechargeBO);
        if(PayStatusEnums.SUCCESS.getCode().equals(rechargeBO.getStatus())){
            //发送消息
            productHandler.sendKafkaMsg(BO2BOConverter.getPayMessageDTO(rechargeBO));
        }
        return rechargeBO.getStatus();
    }

    private void handleRetryStatus(RechargeBO rechargeBO, RechargeFundBO rechargeFee, RechargeFundBO rechargeFund) {
        rechargeBO.getRechargeFundBO().clear();
        if(rechargeFee!=null)
        rechargeBO.getRechargeFundBO().add(rechargeFee);
        if(rechargeFund!=null)
        rechargeBO.getRechargeFundBO().add(rechargeFund);
    }

}
